//
//  IPaParse.swift
//  JKTool
//
//  Created by liuyan on 2023/5/29.
//

import Foundation

extension JKTool {
    struct IpaParse: ParsableCommand {
        static var configuration = CommandConfiguration(
            commandName: "ipaParse",
            _superCommandName: "JKTool",
            abstract: "ipaParse命令用于ipa包大小分析",
            version: "1.0.0"
        )
        
        @Option(name: .shortAndLong, help: "ipa路径")
        var ipaPath: String
        
        @Option(name: .shortAndLong, help: "linkMap路径")
        var linkMapPath: String?
        
        @Option(name: .shortAndLong, help: "分析结果保存路径")
        var resultPath: String?
        
        var parseResult = ""
        var appName = ""
        
        mutating func run() {
            po(tip: "======ipa包大小分析开始======")
            let date = Date.init().timeIntervalSince1970
            
            let fileManager = FileManager.default
            
            guard fileManager.fileExists(atPath: ipaPath) else {
                po(tip: "请选择正确的ipa文件路径", type: .error)
                return
            }
            
            // 获取ipa文件大小
            var ipaFileSize: UInt = 0
            do {
                let attributes = try fileManager.attributesOfItem(atPath: ipaPath)
                ipaFileSize = attributes[.size] as! UInt
            } catch {
                po(tip: "获取文件大小失败\r\n\(error)", type: .error)
                return
            }

            // 清理工作目录
            let workingDirectory = "ipaParse"
            if fileManager.fileExists(atPath: workingDirectory) {
                do {
                    try fileManager.removeItem(atPath: workingDirectory)
                } catch {
                    po(tip: "ipaParse目录清理失败\r\n\(error)", type: .error)
                    return
                }
            }
            
            // 清理结果保存路径
            var resultSavePath = resultPath ?? "ipaParseInfo.txt"
            if fileManager.isDirectory(path: resultSavePath) {
                resultSavePath = "\(resultSavePath)/ipaParseInfo.txt"
            }
            
            if fileManager.fileExists(atPath: resultSavePath) {
                do {
                    try fileManager.removeItem(atPath: resultSavePath)
                } catch {
                    po(tip: "ipaParse文件清理失败\r\n\(error)", type: .error)
                    return
                }
            }
            
            do {
                try fileManager.createDirectory(atPath: workingDirectory, withIntermediateDirectories: true)
            } catch {
                po(tip: "ipaParse目录创建失败\r\n\(error)", type: .error)
                return
            }
            
            do {
                try shellOut(to: ShellOutCommand(string: "unzip \(ipaPath) -d \(workingDirectory)"))
                if let appFilePath = findAppFile(path: workingDirectory) {
                    appName = appFilePath.components(separatedBy: "/").last!.components(separatedBy: ".").first!
                    try fileManager.moveItem(atPath: appFilePath, toPath: "\(workingDirectory)/\(appName).app")
                    
                    // 这里使用appName重新命名了ipa，避免比较的时候名称不同
                    recordFile(atPath: "\(appName).ipa" , size: ipaFileSize)
                }
            } catch  {
                po(tip: "ipa解压失败\r\n\(error)", type: .error)
                return
            }
            
            let currentDirectoryPath = fileManager.currentDirectoryPath
            fileManager.changeCurrentDirectoryPath(workingDirectory)
            _ = sizeOfConentOfFile(path: "\(appName).app")
            fileManager.changeCurrentDirectoryPath(currentDirectoryPath)
            
            do {
                try parseResult.write(toFile: resultSavePath, atomically: true, encoding: .utf8)
                try fileManager.removeItem(atPath: workingDirectory)
            } catch {
                po(tip: "ipa分析结果保存失败\r\n\(error)", type: .error)
                return
            }
            
            po(tip: "======ipa包大小分析完成[\(GlobalConstants.duration(to: date) + " s")]======")
        }
        
        func findAppFile(path: String) -> String? {
            var appFilePath: String? = nil
            let fileManager = FileManager.default
            if path.hasSuffix(".app") {
                return path
            } else if fileManager.isDirectory(path: path) {
                do {
                    let files = try fileManager.contentsOfDirectory(atPath: path)
                    for file in files {
                        appFilePath = findAppFile(path: "\(path)/\(file)")
                        if appFilePath != nil {
                            return appFilePath
                        }
                    }
                } catch {
                    po(tip: "App文件遍历失败\r\n\(error)", type: .error)
                }
            }
            return nil
        }
        
        mutating func recordFile(atPath path: String, size: UInt) {
            parseResult.append(recordFileString(atPath: path, size: size))
        }
        
        func recordFileString(atPath path: String, size: UInt) -> String {
            var sizeStr = ""
            if Double(size) / 1024.0 / 1024.0 > 1 {
                sizeStr = String(format: "%.2fMB", Double(size) / 1024.0 / 1024.0)
            } else {
                sizeStr = String(format: "%.2fKB", Double(size) / 1024.0)
            }
            return String(format: "%@\t%@\r\n", sizeStr, path)
        }

        mutating func sizeOfConentOfFile(path: String) -> UInt {
            let fileManager = FileManager.default
            do {
                let attributes = try fileManager.attributesOfItem(atPath: path)
                let fileSize = attributes[.size] as! UInt
                let fileType = attributes[.type] as! FileAttributeType
                if fileType == .typeDirectory {
                    let sizePlaceHolder = "\(path)###sizePlaceHolder###"
                    parseResult.append(sizePlaceHolder)
                    let subFiles = try fileManager.contentsOfDirectory(atPath: path)
                    var directorySize: UInt = 0
                    for subFile in subFiles {
                        directorySize = directorySize + sizeOfConentOfFile(path: "\(path)/\(subFile)")
                    }
                    parseResult = parseResult.replacingOccurrences(of: sizePlaceHolder, with: recordFileString(atPath: path, size: directorySize))
                    return directorySize
                } else {
                    recordFile(atPath: path, size: fileSize)
                    if path.hasSuffix(".car") {
                        // 分析CAR文件
                        if let assetsParseResult = AssetsParser().parseContent(path: path) {
                            for (name, size) in assetsParseResult {
                                recordFile(atPath: "\(path)/\(name)", size: size)
                            }
                        }
                    } else if path.hasSuffix("/\(appName)") {
                        // 分析linkMap
                        if let linkMapPath {
                            if let linkMapParseResult = LinkMapParser().parseContent(path: linkMapPath) {
                                parseResult.append("# LinkMap Parse Start\r\n")
                                for (name, size) in linkMapParseResult {
                                    recordFile(atPath: "\(path)/\(name)", size: size)
                                }
                                parseResult.append("# LinkMap Parse End\r\n")
                            }
                        }
                    }
                    return fileSize
                }
            } catch {
                po(tip: "ipa分析获取文件大小失败\r\n\(error)", type: .error)
            }
            return 0
        }
    }
}
